package sicnu.cs.ich.user.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import sicnu.cs.ich.api.common.ResultInfo;
import sicnu.cs.ich.api.common.enums.CertType;
import sicnu.cs.ich.api.common.enums.ConfigEnum;
import sicnu.cs.ich.api.domain.dto.CertificationDTO;
import sicnu.cs.ich.api.domain.entity.QiniuFile;
import sicnu.cs.ich.api.domain.entity.UserInfo;
import sicnu.cs.ich.api.domain.form.CertParam;
import sicnu.cs.ich.api.domain.form.CertificateUserForm;
import sicnu.cs.ich.common.exceptions.exception.BusinessException;
import sicnu.cs.ich.common.services.IConfigService;
import sicnu.cs.ich.common.util.ResultInfoUtil;
import sicnu.cs.ich.security.mapper.UserMapper;
import sicnu.cs.ich.user.mapper.CertMapper;
import sicnu.cs.ich.user.service.ICertService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import sicnu.cs.ich.api.common.enums.FileEnum;
import sicnu.cs.ich.api.domain.entity.Certification;
import sicnu.cs.ich.api.domain.entity.CertificationFile;
import sicnu.cs.ich.common.services.IFileUploadService;
import sicnu.cs.ich.security.service.IUserService;

import java.io.FileInputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;

import static sicnu.cs.ich.security.util.SecurityUtil.getCurrentUserId;

/**
 * @author 沈洋 邮箱:1845973183@qq.com
 * @create 2021/11/14-13:26
 **/
@Slf4j
@Service
@RequiredArgsConstructor
public class CertServiceImpl implements ICertService {
    private final CertMapper certMapper;
    private final IFileUploadService fileUploadService;
    private final IUserService userService;
    private final IConfigService configService;
    private final UserMapper userMapper;
    @Transactional( propagation = Propagation.REQUIRES_NEW, rollbackFor = RuntimeException.class)
    //不管该方法是否在事务中，都会开启一个新的事务，不管外部事务是否成功
    //最终都会提交掉该事务，为了保证订单号的唯一性，防止下单失败后订单号的回滚
    public String generateCertId() {
        //订单有16位
        StringBuilder stringBuilder = new StringBuilder();
        //前8位为时间信息，年月日
        LocalDateTime now = LocalDateTime.now();
        String nowDate = now.format(DateTimeFormatter.ISO_DATE).replace("-", "");
        stringBuilder.append(nowDate);
        //中间6位为自增序列
        //获取当前sequence
        int sequence = Integer.parseInt(configService.loadConfig(ConfigEnum.CERT_SEQ));
        configService.storeConfig(ConfigEnum.CERT_SEQ, String.valueOf(sequence + 1));
        //拼接
        String sequenceStr = String.valueOf(sequence);
        stringBuilder.append("0".repeat(Math.max(0, 6 - sequenceStr.length())));
        stringBuilder.append(sequenceStr);
        //最后两位为分库分表位,暂时不考虑
        stringBuilder.append("00");
        return stringBuilder.toString();
    }


    /**
     * 获取用户所有认证信息
     *
     * @param userId 用户id
     */
    @Override
    public List<Certification> findAllCert(Integer userId) {
        return certMapper.findCertByUserId(userId, null);
    }

    @Override
    public Certification findCert(Integer userId, CertType certType) {
        var certifications = certMapper.findCertByUserId(userId, certType.typeId);
        return certifications.size() > 0 ? certifications.get(0) : null;
    }

    /**
     * 删除文件
     *
     * @param certId 认证id
     */
    @Override
    public void deleteFile(String certId) {
        List<CertificationFile> files = certMapper.findCertFiles(certId);
        fileUploadService.deleteFile(files.stream().map(QiniuFile::getFilePath).collect(Collectors.toList()));
        certMapper.deleteCertFile(certId);
    }

    /**
     * 插入或更新认证信息
     *
     * @param cert 认证信息
     * @return 是否处理成功
     */
    @Override
    public boolean updateOrInsert(Certification cert) {
        // 注意：返回值为1时表示插入，返回值为2时表示更新
        return certMapper.insertCert(cert) > 0;
    }

    /**
     * 存放图片信息
     *
     * @param certId 认证id
     * @param images 图片
     */
    @Async
    @Override
    public void insertCertFile(String certId, List<MultipartFile> images) {
        List<CertificationFile> files = new ArrayList<>();
        for (MultipartFile image : images) {
            try {
                String filePath = fileUploadService.uploadFile((FileInputStream) image.getInputStream(), FileEnum.CERT, image.getOriginalFilename());
                CertificationFile cert = new CertificationFile(certId, filePath, image.getContentType());
                files.add(cert);
            } catch (IOException e) {
                log.error(image + "文件上传错误");
            }
        }
        // 不为空时插入
        if (!CollectionUtils.isEmpty(files)) {
            certMapper.insertCertFiles(files);
        }
    }

    /**
     * 个人认证
     */
    @Override
    @Transactional
    public ResultInfo<Void> certUser(CertificateUserForm userForm) {
        Certification cert = findCert(getCurrentUserId(), CertType.PERSON);
        if (cert != null) {
            // 不是第一次认证
            if (cert.getStatus() == 0)
                // 等待认证中
                return ResultInfoUtil.buildError("认证待审核中，请勿重复提交");
            else if (cert.getStatus() == 1)
                return ResultInfoUtil.buildError("认证已通过，请勿重复提交");
                // 修正信息
            else deleteFile(cert.getId());
        } else {
            cert = Certification.builder()
                    .id(generateCertId())
                    .userId(getCurrentUserId())
                    .typeId(CertType.PERSON.typeId)
                    .build();
            cert = new Certification();
            cert.setId(generateCertId());
            cert.setUserId(getCurrentUserId());
            cert.setTypeId(CertType.PERSON.typeId);
            cert.setTypeName(CertType.PERSON.typeName);
        }
        // 认证信息入库等待审核
        if (updateOrInsert(cert)) {
            // 上传七牛云和入库
            insertCertFile(cert.getId(),
                    List.of(userForm.getBackImage(), userForm.getFrontImage()));
            // 更新用户信息
            UserInfo userInfo = UserInfo.builder()
                    .userId(getCurrentUserId())
                    .realName(userForm.getRealName())
                    .idNumber(userForm.getIdNumber())
                    .build();
            userService.updateUserInfo(userInfo);
            return ResultInfoUtil.buildSuccess("上传成功，等待审核");
        }
        return ResultInfoUtil.buildError("上传失败，请重试");
    }

    /**
     * 管理员获取认证待处理认证信息
     *
     * @param param 参数
     * @return 认证列表
     */
    @Override
    public PageInfo<Certification> getCertList(CertParam param) {
        PageHelper.startPage(param.getPageNum(), param.getPageSize());
        List<Certification> certifications = certMapper.selectCert(param);
        if (CollectionUtils.isEmpty(certifications)) {
            throw new NoSuchElementException("无数据");
        }
        return new PageInfo<>(certifications);
    }

    /**
     * 查询认证详情
     *
     * @param certId 认证id
     */
    @Override
    public CertificationDTO getCertDTO(String certId) {
        return certMapper.findCertDTO(certId);
    }

    /**
     * 获取Cert实体
     */
    @Override
    public Certification getCert(String certId) {
        return certMapper.findCert(certId);
    }

    /**
     * 驳回认证
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)
    public boolean refuseCertification(Certification certification) {
        if (certMapper.updateCertStatus(certification.getId(),-1)>0) {
            // 消除用户认证信息
            if (userMapper.updateUserCertInfo(certification.getUserId(),null,null)>0
                    &&userMapper.updateUserCertStatus(certification.getUserId(),0)>0) {
                return true;
            }
            throw new BusinessException("驳回失败");
        }
        return false;
    }

    /**
     * 确认认证
     */
    @Override
    @Transactional
    public boolean confirmCertification(Certification certification) {
        if (certMapper.updateCertStatus(certification.getId(),1)>0) {
            return userMapper.updateUserCertStatus(certification.getUserId(),certification.getTypeId())>0;
        }
        return false;
    }
}
